package logic

import (
	"context"
	"github.com/pkg/errors"
	"go-zero-demo/common/tools/lock"
	"go-zero-demo/user-rpc/excption"
	"go-zero-demo/user-rpc/internal/dao"
	"go-zero-demo/user-rpc/internal/dao/entity"
	"gorm.io/gorm"
	"math/rand"
	"time"

	"go-zero-demo/user-rpc/internal/svc"
	"go-zero-demo/user-rpc/pb"

	"github.com/zeromicro/go-zero/core/logx"
)

type CreateUserLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic {
	return &CreateUserLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *CreateUserLogic) CreateUser(req *pb.CreateUserReq) (resp *pb.CreateUserResp, err error) {
	// 1.newRedisLock
	newRedisLock := lock.NewRedisLock(l.svcCtx.RedisClient, "createUser:1")
	// 2. 可选操作，设置 redislock 过期时间
	newRedisLock.SetExpire(300)
	if ok, lockErr := newRedisLock.AcquireCtx(l.ctx); !ok || lockErr != nil {
		logx.Error("当前有其他用户正在进行操作，请稍后重试")
		return nil, errors.New("当前有其他用户正在进行操作，请稍后重试")
	}
	defer func() {
		recover()
		// 3. 释放锁
		_, releaseErr := newRedisLock.Release()
		if releaseErr != nil {
			logx.Error(releaseErr)
			return
		}
	}()
	i := int(rand.Int31n(1000))
	time.Sleep(time.Duration(i) * time.Millisecond)
	// 1. builder entity
	insertUser, now, userResp, err2 := l.builderUserEntity(req)
	if err2 != nil {
		return userResp, err2
	}
	// 2. process user in db
	insertUserId, txErr, err := l.doCreateUser(req, insertUser, now, err)
	if txErr != nil {
		return nil, txErr
	}
	// 3. return obj
	return &pb.CreateUserResp{UserId: insertUserId}, nil
}

func (l *CreateUserLogic) doCreateUser(req *pb.CreateUserReq, insertUser *entity.UserEntity, now time.Time, err error) (int32, error, error) {
	insertUserId := int32(-1)
	txErr := l.svcCtx.DBEngin.Transaction(func(tx *gorm.DB) error {
		if txErr := tx.Error; txErr != nil {
			return excption.NewErrCode(excption.DbOptError)
		}
		// 1.insert user
		userId, insertErr := dao.CreateUserTx(tx, insertUser)
		if insertErr != nil {
			return excption.NewErrMsg(insertErr.Error())
		}
		// 2.insert role
		insertRoleArr := make([]*entity.UserRoleEntity, 0, 0)
		for _, v := range req.RoleIdArr {
			userRole := entity.UserRoleEntity{}
			userRole.RoleId = int(v)
			userRole.UserId = int(userId)
			userRole.CreateTime = &now
			userRole.UpdateTime = &now
			insertRoleArr = append(insertRoleArr, &userRole)
		}
		_, err = dao.CreateBatchUserRoleTx(tx, insertRoleArr)
		if err != nil {
			return excption.NewErrMsg(insertErr.Error())
		}
		insertUserId = userId
		return nil
	})
	return insertUserId, txErr, err
}

func (l *CreateUserLogic) builderUserEntity(req *pb.CreateUserReq) (*entity.UserEntity, time.Time, *pb.CreateUserResp, error) {
	insertUser := new(entity.UserEntity)
	insertUser.Gender = req.Sex
	insertUser.NickName = req.Username
	now := time.Now()
	insertUser.Password = req.Password
	insertUser.CreateTime = &now
	insertUser.UpdateTime = &now
	insertUser.NickName = req.Nickname
	insertUser.Address = req.Address
	insertUser.HeadUrl = req.HeadUrl
	insertUser.Desc = req.Desc
	insertUser.Mobile = req.Mobile
	parse, parseErr := time.Parse("2006-01-02 15:04:05", req.Birthday)
	if parseErr != nil {
		return nil, time.Time{}, nil, parseErr
	}
	insertUser.Birthday = &parse
	return insertUser, now, nil, nil
}
